/*
 *  Brain.cpp
 *  
 *
 *  Created by Daniel Condon on 23/08/12.
 *  Copyright 2012 __MyCompanyName__. All rights reserved.
 *
 */


#include "Brain.h"
#include <stdio.h>
#include <stdlib.h>
#include <iostream>

const int infectTime = 150;

double sep_threshold = 15.0;
double Range = 40.0;


Brain::Brain(){}

Brain::Brain(double nsize){	
	Brain(0.0, 0.0, nsize, -1);
	vel = 1.0;
	objID = -1;
	dRange = Range;
	isAlive = true;
}

Brain::Brain(double nx, double ny, double nsize, int nID){
	pos.setX(nx);
	pos.setY(ny);
	Brain(pos, nsize, nID);
	vel = 1.0;
	seperation_threshold = sep_threshold;
	detectRange = dRange;
	objID = nID;
	dRange = Range;
	isAlive = true;
}

Brain::Brain(Vec npos, double nsize, int nID){
	pos = npos;
	size = nsize;
	seperation_threshold = sep_threshold;
	detectRange = dRange;
	others.clear();
	seperation.clear();
	alignment.clear();
	cohesion.clear();
	dir.clear();
	vel = 1.0;
	objID = nID;
	dRange = Range;
	spread = false;
	forw = Vec(((double)(rand()%20)-10)/10, ((double)(rand()%20)-10)/10);
	forw = forw.getUnit();
	timeNearInfected=0;
	isAlive = true;
}

void Brain::Kill(){
	isAlive = false;
}	

Brain::~Brain(){
	others.erase(others.begin(), others.end());
}

Vec Brain::getPos(){
	return pos;
}

Vec Brain::getDir(){
	return dir;
}

void Brain::setSize(double nSize){
	size = nSize;
}

double Brain::getSize(){
	return size;
}

double Brain::getRange(){
	return dRange;
}

bool Brain::getAlive(){
	return isAlive;
}

void Brain::AddSeperation(int who, double d){
	seperation += (pos - others.at(who)->getPos())/(d*d);
}

void Brain::AddAlignment(int who){
	alignment += others.at(who)->getDir();
}

void Brain::AddCohesion(int who){
	cohesion += others.at(who)->getPos();
}

bool Brain::canAdd(Brain* other){
	//is self
	bool ret = true;
	if (other->objID == objID) {
		ret = false;
	}else {
		for (int i = 0; i < others.size(); i++) {
			if (others.at(i)->objID == other->objID) {
				ret = false;
				break;
			}
		}
	}
	return ret;
}

void Brain::AddNearby(vector<Brain*> newOthers){
	Clear();
	if (newOthers.size() > 0) {
		for (int i = 0; i < newOthers.size(); i++) {
			if (canAdd(newOthers.at(i))) {
				double d = newOthers.at(i)->getPos().Dist(pos);
				if (d <= dRange) {
					others.push_back(newOthers.at(i));
				}
			}
		}
	}
}

void Brain::Clear(){
	others.clear();	
}

void Brain::Clean(){
	seperation.clear();
	alignment.clear();
	cohesion.clear();
}

void Brain::Sense(){
	double d;
	for (int i = 0; i < others.size(); i++) {
		d = others.at(i)->getPos().Dist(pos);
		
		if (d <= seperation_threshold) {
			if (d != 0.0) {
				AddSeperation(i, d);
			}
		}else {
			AddCohesion(i);
			AddAlignment(i);
		}
	}
	
	if (others.size() > 0) {
		cohesion = (cohesion/(double)others.size()) - pos;
		alignment = alignment/(double)others.size();
	}
}

void Brain::Move(){
	Clean();
	Sense();
	dir = ((seperation*50.0)+(cohesion/10.0)+alignment);
	dir += Vec(((double)(rand()%20)-10)/10, ((double)(rand()%20)-10)/10).getUnit()/10;
	dir = dir.getUnit()/7;
	pos += dir;
	if (pos.getX() + size > 100 || pos.getX() - size < -100 || pos.getY() + size > 100 || pos.getY() - size < -100) {
		pos -= dir - forw;
	}
	virus.Tick();
	if (virus.getTTD() > 500) {
		Kill();
	}
}

int Brain::infectionStatus(){
	if (!isAlive) {
		return 3;
	}
	return virus.hostStatus();
}

void Brain::giveVirus(){
	virus.activateVirus();
}

void Brain::contractVirus(){
	if (timeNearInfected > infectTime) {
		virus.infect();
	}else {
		timeNearInfected++;
	}
}

void Brain::InfectOthers(){
	if (spread) {
		if (virus.canInfect()) {
			for (int i = 0; i < others.size(); i++) {
				if (pos.Dist(others.at(i)->getPos()) <= seperation_threshold) {
					others.at(i)->contractVirus();
				}
			}
		}
		
	}
	spread = true;
}

//DEBUG
vector<Brain*> Brain::connected(){
	return others;
}